/**
 * @brief MCU GPIO 驱动适配层
 * @author 张勇 / 2020-05-13
 */
 
#include <stdint.h>

#include <stm32f10x_gpio.h>
#include <stm32f10x_exti.h>
#include "mcu.h"
#include "_mcu.h"
#include "_mcu_gpio.h"


// MODEy:
#define GPIN_MODE_In				0x00	// 00: Input mode (reset state)
#define GPIN_MODE_Out_10MHz			0x01    // 01: Output mode, max speed 10 MHz.
#define GPIN_MODE_Out_2MHz			0x02    // 10: Output mode, max speed 2 MHz.
#define GPIN_MODE_Out_50MHz			0x03    // 11: Output mode, max speed 50 MHz.

// CNFy:
// In input mode (MODE[1:0]=00):
#define GPIN_CNF_In_Analog			0x00	// 00: Analog mode
#define GPIN_CNF_In_Floating		0x01    // 01: Floating input (reset state)
#define GPIN_CNF_In_PP				0x02    // 10: Input with pull-up / pull-down
// In output mode (MODE[1:0] > 00):
#define GPIN_CNF_Out_PP				0x00    // 00: General purpose output push-pull
#define GPIN_CNF_Out_OD				0x01    // 01: General purpose output Open-drain
#define GPIN_CNF_Out_AF_PP			0x02    // 10: Alternate function output Push-pull
#define GPIN_CNF_Out_AF_OD			0x03    // 11: Alternate function output Open-drain

// GPIN_CFG 可能的模式定义(共15种)
// [3..2]: CNF
// [1..0]: MODE
#define GPIN_CFG_In_Analog			((GPIN_CNF_In_Analog	<<2) | GPIN_MODE_In)
#define GPIN_CFG_In_Floating		((GPIN_CNF_In_Floating	<<2) | GPIN_MODE_In)
#define GPIN_CFG_In_PP				((GPIN_CNF_In_PP		<<2) | GPIN_MODE_In)

#define GPIN_CFG_Out_PP_2MHz		((GPIN_CNF_Out_PP		<<2) | GPIN_MODE_Out_2MHz)
#define GPIN_CFG_Out_OD_2MHz		((GPIN_CNF_Out_OD		<<2) | GPIN_MODE_Out_2MHz)
#define GPIN_CFG_Out_PP_10MHz		((GPIN_CNF_Out_PP		<<2) | GPIN_MODE_Out_10MHz)
#define GPIN_CFG_Out_OD_10MHz		((GPIN_CNF_Out_OD		<<2) | GPIN_MODE_Out_10MHz)
#define GPIN_CFG_Out_PP_50MHz		((GPIN_CNF_Out_PP		<<2) | GPIN_MODE_Out_50MHz)
#define GPIN_CFG_Out_OD_50MHz		((GPIN_CNF_Out_OD		<<2) | GPIN_MODE_Out_50MHz)

#define GPIN_CFG_Out_AF_PP_2MHz		((GPIN_CNF_Out_AF_PP	<<2) | GPIN_MODE_Out_2MHz)
#define GPIN_CFG_Out_AF_OD_2MHz		((GPIN_CNF_Out_AF_OD	<<2) | GPIN_MODE_Out_2MHz)
#define GPIN_CFG_Out_AF_PP_10MHz	((GPIN_CNF_Out_AF_PP	<<2) | GPIN_MODE_Out_10MHz)
#define GPIN_CFG_Out_AF_OD_10MHz	((GPIN_CNF_Out_AF_OD	<<2) | GPIN_MODE_Out_10MHz)
#define GPIN_CFG_Out_AF_PP_50MHz	((GPIN_CNF_Out_AF_PP	<<2) | GPIN_MODE_Out_50MHz)
#define GPIN_CFG_Out_AF_OD_50MHz	((GPIN_CNF_Out_AF_OD	<<2) | GPIN_MODE_Out_50MHz)

static GPIO_TypeDef* _gpio_port(mcu_gpio_num_t num) {
	if(num <= MCU_GPIO_NUM_PA15) { return GPIOA; }
	if(num <= MCU_GPIO_NUM_PB15) { return GPIOB; }
	if(num <= MCU_GPIO_NUM_PC15) { return GPIOC; }
	if(num <= MCU_GPIO_NUM_PD15) { return GPIOD; }
	if(num <= MCU_GPIO_NUM_PE15) { return GPIOE; }
	if(num <= MCU_GPIO_NUM_PF15) { return GPIOF; }
	if(num <= MCU_GPIO_NUM_PG15) { return GPIOG; }
	return NULL;
}

int mcu_gpio_exti_irqn(mcu_gpio_num_t num) {
	uint32_t pin = num & 0x0F;
	if(pin ==  0) { return EXTI0_IRQn;     }
	if(pin ==  1) { return EXTI1_IRQn;     }
	if(pin ==  2) { return EXTI2_IRQn;     }
	if(pin ==  3) { return EXTI3_IRQn;     }
	if(pin ==  4) { return EXTI4_IRQn;     }
	if(pin <=  9) { return EXTI9_5_IRQn;   }
	return EXTI15_10_IRQn;
}

static uint8_t GPIN_ReadIn(GPIO_TypeDef* port, uint16_t pin) {
	return ((port->IDR & (1U<<pin)) != 0);
}

static uint8_t GPIN_ReadOut(GPIO_TypeDef* port, uint16_t pin) {
	return ((port->ODR & (1U<<pin)) != 0);
}

static void GPIN_WriteOut(GPIO_TypeDef* port, uint16_t pin, uint8_t level) {
	if(level) {
		port->BSRR = (1U<<pin);
	} else {
		port->BRR  = (1U<<pin);
	}
}

static void GPIN_Toggle(GPIO_TypeDef* port, uint16_t pin) {
	port->ODR ^= (1U<<pin);
}

// GPIN CR Register Mask
#define GPIN_CR_MASK(pin) ((uint32_t)0x0F<<(pin<<2))

static void GPIN_CR_Write(GPIO_TypeDef* port, uint16_t pin, uint8_t cfg) {
	if(pin < 8) {
		port->CRL &= ~GPIN_CR_MASK(pin);
		port->CRL |= (uint32_t)cfg<<(pin<<2);
	} else {
		pin = pin-8;
		port->CRH &= ~GPIN_CR_MASK(pin);
		port->CRH |= (uint32_t)cfg<<(pin<<2);
	}
}

static uint8_t GPIN_CR_Read(GPIO_TypeDef* port, uint16_t pin) {
	if(pin < 8) {
		return ((port->CRL & GPIN_CR_MASK(pin)) >> (pin<<2)) & 0x0F;
	} else {
		pin = pin-8;
		return ((port->CRH & GPIN_CR_MASK(pin)) >> (pin<<2)) & 0x0F;
	}
}

std_err_t GPIO_Config(mcu_gpio_num_t num, mcu_gpio_mode_t mode) {
	GPIO_TypeDef* port = _gpio_port(num);
	uint16_t pin = num & 0x0F;
	uint8_t cfg;
	switch(mode) {
		case MCU_GPIO_MODE_INPUT_PU : cfg = GPIN_CFG_In_PP;    		break;
		case MCU_GPIO_MODE_INPUT_PD : cfg = GPIN_CFG_In_PP;    		break;
		case MCU_GPIO_MODE_INPUT    : cfg = GPIN_CFG_In_Floating; 	break;
		case MCU_GPIO_MODE_OUTPUT_PP: cfg = GPIN_CFG_Out_PP_50MHz;  break;
		case MCU_GPIO_MODE_OUTPUT   : cfg = GPIN_CFG_Out_OD_50MHz;  break;
		default: return STD_ERR_INVALID_ARG;
	}
	GPIN_CR_Write(port, pin, cfg);
	if(mode == MCU_GPIO_MODE_INPUT_PU) { GPIN_WriteOut(port, pin, 1); }
	if(mode == MCU_GPIO_MODE_INPUT_PD) { GPIN_WriteOut(port, pin, 0); }
	return STD_ERR_OK;
}

std_err_t GPIO_SetLevel(mcu_gpio_num_t num, uint8_t level) {
	GPIO_TypeDef* port = _gpio_port(num);
	uint16_t pin = num & 0x0F;
	GPIN_WriteOut(port, pin, level);
	return STD_ERR_OK;
}

uint8_t GPIO_GetLevel(mcu_gpio_num_t num) {
	GPIO_TypeDef* port = _gpio_port(num);
	uint16_t pin = num & 0x0F;
	return GPIN_ReadIn(port, pin);
}

uint8_t GPIO_BidiIn(mcu_gpio_num_t num) {
	GPIO_TypeDef* port = _gpio_port(num);
	uint16_t pin = num & 0x0F;
	uint8_t cfg = GPIN_CR_Read(port, pin);
	
	// 如果当前配置不是 In_PP 模式，则先配置为 IN_PU 模式
	if(cfg != GPIN_CFG_In_PP) { GPIN_CR_Write(port, pin, GPIN_CFG_In_PP); GPIN_WriteOut(port, pin, 1); }
	// 读取并返回输入
	return GPIN_ReadIn(port, pin);
}

std_err_t GPIO_BidiOut(mcu_gpio_num_t num, uint8_t level) {
	GPIO_TypeDef* port = _gpio_port(num);
	uint16_t pin = num & 0x0F;
	uint8_t cfg = GPIN_CR_Read(port, pin);
	
	// !!! 注意：要先设置输出电平，再修改为输出模式，以避免产生毛刺 !!!
	GPIN_WriteOut(port, pin, level);
	// 如果当前配置不是 Out_PP 模式，则配置为 OUT_PP 模式
	if(cfg != GPIN_CFG_Out_PP_50MHz) { GPIN_CR_Write(port, pin, GPIN_CFG_Out_PP_50MHz); }
	return STD_ERR_OK;
}

// 默认的 EXTI 中断处理
_RAMCODE_ void EXTI0_IRQHandler(void)     { mcu_call_irq_handlers(EXTI0_IRQn);     EXTI_ClearITPendingBit(EXTI_Line0); }
_RAMCODE_ void EXTI1_IRQHandler(void)     { mcu_call_irq_handlers(EXTI1_IRQn);     EXTI_ClearITPendingBit(EXTI_Line1); }
_RAMCODE_ void EXTI2_IRQHandler(void)     { mcu_call_irq_handlers(EXTI2_IRQn);     EXTI_ClearITPendingBit(EXTI_Line2); }
_RAMCODE_ void EXTI3_IRQHandler(void)     { mcu_call_irq_handlers(EXTI3_IRQn);     EXTI_ClearITPendingBit(EXTI_Line3); }
_RAMCODE_ void EXTI4_IRQHandler(void)     { mcu_call_irq_handlers(EXTI4_IRQn);     EXTI_ClearITPendingBit(EXTI_Line4); }
_RAMCODE_ void EXTI9_5_IRQHandler(void)   { mcu_call_irq_handlers(EXTI9_5_IRQn);   EXTI_ClearITPendingBit(EXTI_Line9 | EXTI_Line8 | EXTI_Line7 | EXTI_Line6 | EXTI_Line5); }
_RAMCODE_ void EXTI15_10_IRQHandler(void) { mcu_call_irq_handlers(EXTI15_10_IRQn); EXTI_ClearITPendingBit(EXTI_Line15 | EXTI_Line14 | EXTI_Line13 | EXTI_Line12 | EXTI_Line11 | EXTI_Line10); }

int GPIO_EXTI_IRQn(mcu_gpio_num_t num) {
	uint16_t pin = num & 0x0F;
	if(pin == 0) { return EXTI0_IRQn;   }
	if(pin == 1) { return EXTI1_IRQn;   }
	if(pin == 2) { return EXTI2_IRQn;   }
	if(pin == 3) { return EXTI3_IRQn;   }
	if(pin == 4) { return EXTI4_IRQn;   }
	if(pin < 10) { return EXTI9_5_IRQn; }
	return EXTI15_10_IRQn;
}

std_err_t GPIO_EXTI_Enable(mcu_gpio_num_t num, mcu_gpio_intr_type_t trig, uint16_t _res) {
	uint8_t portSource = num >> 4;
	uint8_t pinSource = num & 0x0F;
	uint32_t extiLine = 1U << (num & 0x0F);
	
	// Selects the GPIO pin used as EXTI Line.
	// !!! 注意：此函数要配置 AFIO->EXTICR 寄存器，在调用此函数前，必须使能 AFIO 时钟 !!!
	GPIO_EXTILineConfig(portSource, pinSource);

	// 设置 EXTI 参数
	EXTI_InitTypeDef exti;
	EXTI_StructInit(&exti);
	exti.EXTI_Line = extiLine;
	exti.EXTI_Mode = EXTI_Mode_Interrupt;
	exti.EXTI_Trigger = (EXTITrigger_TypeDef)trig;
	exti.EXTI_LineCmd = ENABLE;
	EXTI_Init(&exti);
	return STD_ERR_OK;
}

std_err_t GPIO_EXTI_Disable(mcu_gpio_num_t num) {
	uint32_t extiLine = 1U << (num & 0x0F);

	// 设置 EXTI 参数
	EXTI_InitTypeDef exti;
	EXTI_StructInit(&exti);
	exti.EXTI_Line = extiLine;
	exti.EXTI_Mode = EXTI_Mode_Interrupt;
	exti.EXTI_LineCmd = DISABLE;
	EXTI_Init(&exti);
	return STD_ERR_OK;
}

std_err_t GPIO_EXTI_ToggleTrigLevel(mcu_gpio_num_t num) {
	uint32_t extiLine = 1U << (num & 0x0F);
	
	// 如果当前配置为双沿触发，则返回错误
    if((EXTI->RTSR & extiLine) && (EXTI->FTSR & extiLine)) { return STD_ERR_INVALID_STATE; }
	// 如果当前配置未配置任何边沿触发，则返回错误
	if(!(EXTI->RTSR & extiLine) && !(EXTI->FTSR & extiLine)) { return STD_ERR_INVALID_STATE; }
	
	if(EXTI->RTSR & extiLine) {
		// 当前是上升沿触发，则修改为下降沿触发
		EXTI->RTSR &= (~extiLine);
		EXTI->FTSR |= extiLine;
	} else {
		// 当前是下降沿触发，则修改为上升沿触发
		EXTI->FTSR &= (~extiLine);
		EXTI->RTSR |= extiLine;
	}
	return STD_ERR_OK;
}

uint8_t GPIO_EXTI_Flag(mcu_gpio_num_t num) {
	uint32_t extiLine = 1U << (num & 0x0F);
	return EXTI_GetITStatus(extiLine);
}

std_err_t GPIO_EXTI_Clear(mcu_gpio_num_t num) { 
	uint32_t extiLine = 1U << (num & 0x0F);
	EXTI_ClearITPendingBit(extiLine);
	return STD_ERR_OK;
}

/// GPIO 端口配置为 PWM 输出
/// @freq 频率(Hz)
/// @duty 高电平的占空比(0.01%)
std_err_t GPIO_ConfigAsPwm(mcu_gpio_num_t num, uint32_t freq, uint32_t duty);
/// 设置 PWM 高电平的占空比(0.01%)
std_err_t GPIO_PwmSetDuty(mcu_gpio_num_t num, uint32_t duty);
/// 获取 PWM 高电平的占空比(0.01%)
uint32_t       GPIO_PwmGetDuty(mcu_gpio_num_t num);
/// 启动 PWM 输出
std_err_t GPIO_PwmStart(mcu_gpio_num_t num);
/// 停止 PWM 输出
std_err_t GPIO_PwmStop(mcu_gpio_num_t num);
